﻿/*
Copyright (c) 2008 William Duff

Permission is hereby granted, free of charge, to any person obtaining a copy 
of this software and associated documentation files (the "Software"), to deal 
in the Software without restriction, including without limitation the rights 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all 
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.
*/

using System;
using System.Collections.Generic;
using Microsoft.WindowsLive.PublishPlugins;
using System.Xml;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Net;
using LiveUpload.YouTube.Properties;
using System.Diagnostics;

namespace LiveUpload.YouTube
{
    public class YouTubePublishPlugin : IPublishPlugin
    {
        /// <summary>
        /// Indicates whether or not a publishing plug-in has publishing results available to 
        /// display to the user.
        /// </summary>
        /// <param name="sessionXml">
        /// An XmlDocument containing session specific information for this publish session.
        /// </param>
        /// <returns>
        /// true if the publishing plug-in has publishing results to display; otherwise, false.
        /// </returns>
        public bool HasPublishResults(XmlDocument sessionXml)
        {
            // Encapsulate sessionXml.
            SessionData sessionData = new SessionData(sessionXml);

            // Return true if at least one upload has a URL.
            return sessionData.ItemSet.Exists(delegate(Item mediaObject)
            {
                return mediaObject.Url != string.Empty;
            });
        }

        /// <summary>
        /// Indicates whether or not a publishing plug-in has summary information available to 
        /// display to the user.
        /// </summary>
        /// <param name="sessionXml">
        /// An XmlDocument containing session specific information for this publish session.
        /// </param>
        /// <returns>
        /// true if the publishing plug-in has summary information to display; otherwise, false.
        /// </returns>
        public bool HasSummaryInformation(XmlDocument sessionXml)
        {
            // Encapsulate sessionXml.
            SessionData sessionData = new SessionData(sessionXml);

            // Return true if there is at least one item in the log.
            return sessionData.Log.Count > 0;
        }

        /// <summary>
        /// Directs the publishing plug-in to display publishing results.
        /// </summary>
        /// <param name="sessionXml">
        /// An XmlDocument containing session specific information for this publish session.
        /// </param>
        public void LaunchPublishResults(XmlDocument sessionXml)
        {
            // Encapsulate sessionXml.
            SessionData sessionData = new SessionData(sessionXml);

            // Find the first item with a URL.
            Item video = sessionData.ItemSet.Find(delegate(Item mediaObject)
            {
                return mediaObject.Url != string.Empty;
            });

            // Direct the user to the video's URL.
            Process.Start(video.Url);
        }

        /// <summary>
        /// Directs the publishing plug-in to publish a specified media object.
        /// </summary>
        /// <param name="parentWindow">
        /// A handle to the parent window that will be used to display any UI to the user.
        /// </param>
        /// <param name="mediaObjectId">
        /// A string identifying the media object to be published. 
        /// </param>
        /// <param name="stream">
        /// A stream for the contents of the media object to be published.
        /// </param>
        /// <param name="sessionXml">
        /// An XmlDocument containing session specific information for this publish session.
        /// </param>
        /// <param name="publishProperties">
        /// A reference an IPublishProperties interface, which can be used to access a media 
        /// object's metadata store.
        /// </param>
        /// <param name="callback">
        /// A reference to an IPublishProgressCallback callback interface that can be used to 
        /// communicate progress information to the user.
        /// </param>
        /// <param name="cancelEvent">
        /// A thread synchronization event that will signal in the event a user clicks the cancel
        /// button during the upoad.
        /// </param>
        /// <returns>
        /// true if the specified media object was successfully published; otherwise, false.
        /// </returns>
        public bool PublishItem(IWin32Window parentWindow, string mediaObjectId, Stream stream, 
            XmlDocument sessionXml, IPublishProperties publishProperties, 
            IPublishProgressCallback callback, EventWaitHandle cancelEvent)
        {
            // Encapsulate sessionXml.
            SessionData sessionData = new SessionData(sessionXml);

            // Initiate our communication interface with YouTube.
            YouTubeApi youTube = new YouTubeApi(sessionData.DeveloperKey, sessionData.ClientId);

            // Find the video we are uploading.
            Item video = sessionData.ItemSet.Find(delegate(Item mediaObject)
            {
                return mediaObject.Id == mediaObjectId;
            });

            UploadContext cookie = null;
            int megabytesUploaded = 0;
            int megabytesSize = video.FileSize / 1024;

            try
            {
                // Initiate upload.
                cookie = 
                    youTube.PrepareUpload(sessionData.User, stream, video, sessionData.IsPublic);

                // Be more specific about the size of the upload.
                megabytesSize = (int)(cookie.Request.ContentLength / (1024 * 1024));

                // Main upload loop.
                while (cookie.UploadChunk())
                {
                    callback.SetPublishProgress(cookie.PercentComplete);
                    megabytesUploaded = (int)(cookie.CurrentPosition / (1024 * 1024));

                    // If the user hit the 'Cancel' button.
                    if (callback.Canceled())
                    {
                        return false;
                    }
                }

                // YouTube's response to the upload includes a URL. We'll give the user the option
                // to go to this URL at the end of the session.
                video.Url = youTube.GetUploadedVideoUrl(cookie);
            }
            catch (YouTubeApiException e)
            {
                string error = string.Format(Resources.LogFailure, video.OriginalFileName, 
                    e.Message);
                sessionData.Log.Add(new LogItem(false, error, megabytesUploaded, megabytesSize));
                sessionData.Save();

                return false;
            }

            // If we got here, the upload was a success.
            string message = string.Format(Resources.LogSuccess, video.OriginalFileName);
            sessionData.Log.Add(new LogItem(true, message, megabytesUploaded, megabytesSize));
            sessionData.Save();

            return true;
        }

        /// <summary>
        /// Directs the publishing plug-in to show its configuration UI.
        /// </summary>
        /// <param name="parentWindow">
        /// A handle to the parent window that will be used to display any UI to the user.
        /// </param>
        /// <param name="sessionXml">
        /// An XmlDocument containing session specific information for this publish session.
        /// </param>
        /// <param name="persistXml">
        /// An XmlDocument containing persistent information for this plug-in.
        /// </param>
        /// <param name="publishProperties">
        /// A reference an IPublishProperties interface, which can be used to access a media 
        /// object's metadata store.
        /// </param>
        /// <returns>
        /// true if the publishing operation should continue; false if there is an error, or the 
        /// user cancelled the publishing operation.
        /// </returns>
        public bool ShowConfigurationSettings(IWin32Window parentWindow, XmlDocument sessionXml, 
            XmlDocument persistXml, IPublishProperties publishProperties)
        {
            // Encapsulate sessionXml and persistXml.
            SessionData sessionData = new SessionData(sessionXml);
            PersistentData persistData = new PersistentData(persistXml);

            // See if an image is selected.
            bool imageSelected = sessionData.ItemSet.Exists(delegate(Item mediaObject)
            {
                return mediaObject.PerceivedType == "image";
            });

            // Bail out with an error if an image, or more than one item, is selected.
            if (imageSelected || sessionData.ItemSet.Count > 1)
            {
                MessageBox.Show(parentWindow, Resources.ItemSelectionError, Resources.PluginTitle, 
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
                return false;
            }

            // Launch ConfigureForm and return the result.
            using (ConfigureForm configureForm = new ConfigureForm(sessionData, persistData))
            {
                return configureForm.ShowDialog(parentWindow) == DialogResult.OK;
            }
        }

        /// <summary>
        /// Directs the publishing plug-in to display summary information.
        /// </summary>
        /// <param name="parentWindow">
        /// A handle to the parent window that will be used to display the summary information to
        /// the user.
        /// </param>
        /// <param name="sessionXml">
        /// An XmlDocument containing session specific information for this publish session.
        /// </param>
        public void ShowSummaryInformation(IWin32Window parentWindow, XmlDocument sessionXml)
        {
            // Encapsulate sessionXml.
            SessionData sessionData = new SessionData(sessionXml);

            // Launch the SummaryForm.
            using (SummaryForm summaryForm = new SummaryForm(sessionData))
            {
                summaryForm.ShowDialog(parentWindow);
            }
        }
    }
}
